/**
* Component - Foundation Object for Mapper XML Components
*
* Copyright (c) 2002
* Marty Phelan, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.taursys.xml;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import com.taursys.xml.event.RenderException;
import com.taursys.util.MapperComponent;
import com.taursys.xml.event.*;
/**
* <p>This is the foundation object for MapperXML components. All components
* share this base abstract class. It provides common attributes and basic
* event dispatching methods with limited implementation.
* </p>
* <p>This object defines the following properties:</p>
* <ul>
* <li><code>parent</code> - the parent <code>Container</code> for this
* <code>Component</code>. This property is set by the <code>Container</code>
* methods: <code>add</code> and <code>remove</code>.
* </li>
* <li><code>visible</code> - this property is used by render subcomponents to
* determine whether to hide or show this component during rendering.
* </li>
* </ul>
* <p>This object is designed to respond to 5 types of events. The following
* are the events this component is designed to respond to:</p>
* <ul>
* <li><code>ParameterEvent</code> - this event is generated when parameter
* values are available for this component. A <code>ParameterEvent</code>
* differs from an <code>InputEvent</code> only by WHEN it is dispatched.
* <code>ParameterEvents</code> are normally dispatched before
* <code>InputEvents</code>
* </li>
* <li><code>InputEvent</code> - this event is generated when input
* values are available for this component. An <code>InputEvent</code>
* differs from a <code>ParameterEvent</code> only by WHEN it is dispatched.
* <code>InputEvents</code> are normally dispatched after
* <code>ParameterEvents</code>
* </li>
* <li><code>TriggerEvent</code> - this event is generated when a specific
* name/value pair appears (or does not appear) in the request.
* </li>
* <li><code>RenderEvent</code> - this event is generated when it is time
* for components to render their value to the document.
* </li>
* <li><code>RecycleEvent</code> - this event is generated after the
* request/response cycle as a signal that components should return to a
* default or initial state. Components may also modify the document as
* part of their response to this event.
* </li>
* </ul>
* <p>In order to be notified of these events, this component must register
* with each of the appropriate dispatchers. This is done by adding/removing
* the types of events you wish to be notified of to the <code>eventTypeList</code>.
* This is done by invoking the <code>addEventType</code> or
* <code>removeEventType</code> method with the fully qualified class name
* of the desired event. The <code>eventTypeList</code> is used by the
* <code>addNotify</code> and <code>removeNotify</code> methods which are
* invoked by the <code>Container</code> during the <code>add</code> and
* <code>remove</code> <code>Container</code> methods.
* </p>
* <p>This component contains a 2 general purpose methods modeled after
* AWT and Swing: <code>dispatchEvent</code> and <code>processEvent</code>.
* These 2 methods simply call the specific processXXXEvent method based
* on the event type.
* </p>
* <p>For each of the specific event types, there are a set of 3 related
* methods.</p>
* <ul>
* <li><code>processXXXEvent</code> - this method is where you would provide
* the necessary behavior to respond to the event. This abstract implementation
* simply propagates the event to registered listeners. You should override
* this method to provide appropriate behavior.
* </li>
* <li><code>addXXXListener</code> - registers the given listener with this
* component to be notified whenever this component has began/ended processing
* the event.
* </li>
* <li><code>removeXXXListener</code> - unregisters the given listener so it
* will no longer be notified.
* </li>
* </ul>
*/
public abstract class Component implements MapperComponent {
private transient ArrayList parameterListeners;
private transient ArrayList inputListeners;
private transient ArrayList renderListeners;
private transient ArrayList recycleListeners;
private transient ArrayList triggerListeners;
/** Parent container for this component - package visibility */
com.taursys.xml.Container parent;
private boolean visible = true;
private ArrayList eventTypeList = new ArrayList();
private boolean notifySet = false;
/**
* Constructs a component
*/
public Component() {
}
// =======================================================================
// PROPERTY ACCESSOR METHODS
// =======================================================================
/**
* Returns the parent container of this component else null.
*/
public com.taursys.xml.Container getParent() {
return parent;
}
/**
* Set the indicator whether or not this component should be rendered visible.
* This indicator is only meaningful for components which are in the Document.
* @param visible the indicator whether or not this component should be rendered visible.
*/
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* Get the indicator whether or not this component should be rendered visible.
* This indicator is only meaningful for components which are in the Document.
* @return indicator whether or not this component should be rendered visible.
*/
public boolean isVisible() {
return visible;
}
// =======================================================================
// Event Notification Registration Methods
// =======================================================================
/**
* Removes the given <code>eventType</code> from the event type list.
* The event type list is used by <code>addNotify</code> and
* <code>removeNotify</code> methods to register/un-register with
* <code>Dispatchers</code> for events. The <code>eventType</code> is the
* fully qualified class name of the event. This method does not
* re-register with <code>Dispatchers</code>. You must invoke the
* <code>addNotify</code> and <code>removeNotify</code> methods to
* register with <code>Dispatchers</code>.
* @param eventType fully qualified class name of the Event
*/
protected void removeEventType(String eventType) {
if (eventTypeList.contains(eventType)) {
eventTypeList.remove(eventType);
}
}
/**
* Adds the given <code>eventType</code> to the event type list.
* The event type list is used by <code>addNotify</code> and
* <code>removeNotify</code> methods to register/un-register with
* <code>Dispatchers</code> for events. The <code>eventType</code> is the
* fully qualified class name of the event. This method does not
* re-register with <code>Dispatchers</code>. You must invoke the
* <code>addNotify</code> and <code>removeNotify</code> methods to
* register with <code>Dispatchers</code>.
* @param eventType fully qualified class name of the Event
*/
protected void addEventType(String eventType) {
if (!eventTypeList.contains(eventType)) {
eventTypeList.add(eventType);
}
}
/**
* Registers this component with dispatcher to be notified of ParameterEvents
* This method invokes the lazyAddNotify method to perform the work
*/
public void addNotify() {
lazyAddNotify();
}
/**
* Un-Registers this component with dispatcher.
* This method invokes the lazyRemoveNotify method to perform the work
*/
public void removeNotify() {
lazyRemoveNotify();
}
/**
* Conditionally registers this component with dispatchers for the event types
* contained in the <code>eventTypeList</code>. In order to be notified, this
* component must have a parent and the notifySet flag must be false.
*/
protected void lazyAddNotify() {
if (!notifySet && parent != null) {
// Register with each Dispatcher in eventTypeList
Iterator iter = eventTypeList.iterator();
while (iter.hasNext()) {
Dispatcher dispatcher = parent.getDispatcher((String)iter.next());
if (dispatcher != null) {
dispatcher.addNotify(this);
}
}
notifySet = true;
}
}
/**
* Conditionally un-registers this component from dispatcher for the event types
* contained in the <code>eventTypeList</code>.
* Only un-registers if it WAS registered and parent is not null.
*/
protected void lazyRemoveNotify() {
if (notifySet && parent != null) {
// Un-register with each Dispatcher in eventTypeList
Iterator iter = eventTypeList.iterator();
while (iter.hasNext()) {
Dispatcher dispatcher = parent.getDispatcher((String)iter.next());
if (dispatcher != null)
dispatcher.removeNotify(this);
}
notifySet = false;
}
}
/**
* Gets indicator of whether or not notification has been setup.
* This flag is set to true after addNotify has successfully registered,
* and set to false after removeNotify has successfully un-registered.
* @return indicator if notification has been setup.
*/
protected boolean isNotifySet() {
return notifySet;
}
// =======================================================================
// GENERAL EVENT PROCESSING
// =======================================================================
/**
* Dispatches an event to this component or one of its subcomponents.
* This method invokes processEvent before returning.
*/
public void dispatchEvent(EventObject e) throws Exception {
processEvent(e);
}
/**
* Processes given event by invoking the appropriate processX__Event method.
* The appropriate event type is determined by the class of the given
* EventObject.
*/
protected void processEvent(EventObject e) throws Exception {
// In awt, selected events are dispatched to the specific processXxxEvent
if (e instanceof InputEvent)
processInputEvent((InputEvent)e);
if (e instanceof ParameterEvent)
processParameterEvent((ParameterEvent)e);
if (e instanceof RenderEvent)
processRenderEvent((RenderEvent)e);
if (e instanceof TriggerEvent)
processTriggerEvent((TriggerEvent)e);
if (e instanceof RecycleEvent)
processRecycleEvent((RecycleEvent)e);
}
// =======================================================================
// PARAMETER EVENT METHODS
// =======================================================================
/**
* Processes a given ParameterEvent. This implementation simply propagates
* the ParameterEvent to registered listeners. Components who need to respond
* to ParameterEvents should override this method.
*/
protected void processParameterEvent(ParameterEvent e) throws Exception {
fireParameterReceived(e);
}
/**
* Removes given listener from notification list for ParameterEvents
*/
public void removeParameterListener(ParameterListener l) {
if (parameterListeners != null && parameterListeners.contains(l)) {
parameterListeners.remove(l);
}
}
/**
* Adds given listener to notification list for ParameterEvents
*/
public void addParameterListener(ParameterListener l) {
if (parameterListeners != null) {
if (!parameterListeners.contains(l)) {
parameterListeners.add(l);
}
} else {
parameterListeners = new ArrayList();
parameterListeners.add(l);
}
}
/**
* Notifies all registered listeners of a given ParameterEvent
*/
protected void fireParameterReceived(ParameterEvent e) throws Exception {
if (parameterListeners != null) {
Iterator iter = parameterListeners.iterator();
while (iter.hasNext()) {
((ParameterListener)iter.next()).parameterReceived(e);
}
}
}
// =======================================================================
// INPUT EVENT METHODS
// =======================================================================
/**
* Processes a given InputEvent. This implementation simply propagates
* the InputEvent to registered listeners. Components who need to respond
* to InputEvents should override this method.
*/
protected void processInputEvent(InputEvent e) throws Exception {
fireInputReceived(e);
}
/**
* Removes given listener from notification list for InputEvents
*/
public synchronized void removeInputListener(InputListener l) {
if (inputListeners != null && inputListeners.contains(l)) {
inputListeners.remove(l);
}
}
/**
* Adds given listener to notification list for InputEvents
*/
public synchronized void addInputListener(InputListener l) {
if (inputListeners != null) {
if (!inputListeners.contains(l)) {
inputListeners.add(l);
}
} else {
inputListeners = new ArrayList();
inputListeners.add(l);
}
}
/**
* Notifies all registered listeners of a given InputEvent
*/
protected void fireInputReceived(InputEvent e) throws Exception {
if (inputListeners != null) {
Iterator iter = inputListeners.iterator();
while (iter.hasNext()) {
((InputListener)iter.next()).inputReceived(e);
}
}
}
// =======================================================================
// RENDER EVENT METHODS
// =======================================================================
/**
* Processes a given RenderEvent. This implementation simply propagates
* the RenderEvent to registered listeners. Components who need to respond
* to RenderEvents should override this method.
* @param e the RenderEvent to process
* @throws RenderException for any problems during rendering
*/
public void processRenderEvent(RenderEvent e) throws RenderException {
fireRender(e);
}
/**
* Removes given listener from notification list for RenderEvents
*/
public synchronized void removeRenderListener(RenderListener l) {
if (renderListeners != null && renderListeners.contains(l)) {
renderListeners.remove(l);
}
}
/**
* Adds given listener to notification list for RenderEvents
*/
public synchronized void addRenderListener(RenderListener l) {
if (renderListeners != null) {
if (!renderListeners.contains(l)) {
renderListeners.add(l);
}
} else {
renderListeners = new ArrayList();
renderListeners.add(l);
}
}
/**
* Notifies all registered listeners of a given RenderEvent
*/
protected void fireRender(RenderEvent e) throws RenderException {
if (renderListeners != null) {
Iterator iter = renderListeners.iterator();
while (iter.hasNext()) {
((RenderListener)iter.next()).render(e);
}
}
}
// =======================================================================
// TRIGGER EVENT METHODS
// =======================================================================
/**
* Processes a given TriggerEvent. This implementation simply propagates
* the TriggerEvent to registered listeners. Components who need to respond
* to TriggerEvents should override this method.
*/
protected void processTriggerEvent(TriggerEvent e) throws Exception {
fireActionPerformed(e);
}
/**
* Removes given listener from notification list for TriggerEvents
*/
public synchronized void removeTriggerListener(TriggerListener l) {
if (triggerListeners != null && triggerListeners.contains(l)) {
triggerListeners.remove(l);
}
}
/**
* Adds given listener to notification list for TriggerEvents
*/
public synchronized void addTriggerListener(TriggerListener l) {
if (triggerListeners != null) {
if (!triggerListeners.contains(l)) {
triggerListeners.add(l);
}
} else {
triggerListeners = new ArrayList();
triggerListeners.add(l);
}
}
/**
* Notifies all registered listeners of a given TriggerEvent
*/
protected void fireActionPerformed(TriggerEvent e) throws Exception {
if (triggerListeners != null) {
Iterator iter = triggerListeners.iterator();
while (iter.hasNext()) {
((TriggerListener)iter.next()).actionPerformed(e);
}
}
}
// =======================================================================
// RECYCLE EVENT METHODS
// =======================================================================
/**
* Processes a given RecycleEvent. This implementation simply propagates
* the RecycleEvent to registered listeners. Components who need to respond
* to RecycleEvents should override this method.
* @param e the RecycleEvent to process
* @throws RecycleException for any problems during recycleing
*/
public void processRecycleEvent(RecycleEvent e) throws RecycleException {
fireRecycle(e);
}
/**
* Removes given listener from notification list for RecycleEvents
*/
public synchronized void removeRecycleListener(RecycleListener l) {
if (recycleListeners != null && recycleListeners.contains(l)) {
recycleListeners.remove(l);
}
}
/**
* Adds given listener to notification list for RecycleEvents
*/
public synchronized void addRecycleListener(RecycleListener l) {
if (recycleListeners != null) {
if (!recycleListeners.contains(l)) {
recycleListeners.add(l);
}
} else {
recycleListeners = new ArrayList();
recycleListeners.add(l);
}
}
/**
* Notifies all registered listeners of a given RecycleEvent
*/
protected void fireRecycle(RecycleEvent e) throws RecycleException {
if (recycleListeners != null) {
Iterator iter = recycleListeners.iterator();
while (iter.hasNext()) {
((RecycleListener)iter.next()).recycle(e);
}
}
}
}